package com.gitee.wsl.func.filter.bloomfilter

import com.gitee.wsl.func.filter.bloomfilter.api.BloomFilter
import com.gitee.wsl.func.filter.bloomfilter.api.Hasher
import com.gitee.wsl.func.filter.bloomfilter.api.IndexExtractor
import com.gitee.wsl.func.filter.bloomfilter.hasher.EnhancedDoubleHasher
import com.gitee.wsl.func.predicate.Predicate
import kotlin.random.Random


/**
 * A collection of methods and statics that represent standard hashers in testing.
 */
object TestingHashers {
    /**
     * Hasher that increments from 1.
     */
    val FROM1: Hasher = IncrementingHasher(1, 1)

    /**
     * Hasher that increments from 11.
     */
    val FROM11: Hasher = IncrementingHasher(11, 1)

    /**
     * Merge several Hashers together into a single Bloom filter.
     * @param <T> The type of bloom filter.
     * @param filter The Bloom filter to populate
     * @param hashers The hashers to merge
     * @return `filter` for chaining
    </T> */
    fun <T : BloomFilter<T>> mergeHashers(filter: T, vararg hashers: Hasher): T {
        for (h in hashers) {
            filter.merge(h)
        }
        return filter
    }

    /**
     * Enables all bits in the filter.
     * @param <T> the Bloom filter type.
     * @param filter the Bloom filter to populate
     * @return `filter` for chaining
    </T> */
    fun <T : BloomFilter<T>> populateEntireFilter(filter: T): T {
        return TestingHashers.populateRange(filter, 0, filter.shape.numberOfBits - 1)
    }

    /**
     * Merge `from1` and `from11` into a single Bloom filter.
     * @param <T> The type of bloom filter.
     * @param filter The Bloom filter to populate
     * @return `filter` for chaining
    </T> */
    fun <T : BloomFilter<T>> populateFromHashersFrom1AndFrom11(filter: T): T {
        return TestingHashers.mergeHashers(filter, FROM1, FROM11)
    }

    /**
     * Enables all bits in a range (inclusive).
     * @param <T> the Bloom filter type.
     * @param filter the Bloom filter to populate
     * @param start the starting bit to enable.
     * @param end the last bit to enable.
     * @return `filter` for chaining
    </T> */
    fun <T : BloomFilter<T>> populateRange(filter: T, start: Int, end: Int): T {
        filter.merge(IndexExtractor { p: Predicate<Int> ->
            for (i in start..end) {
                if (!p.test(i)) {
                    return@IndexExtractor false
                }
            }
            true
        })
        return filter
    }

    /**
     * Creates an EnhancedDoubleHasher hasher from 2 random longs.
     */
    fun randomHasher(): Hasher {
        return EnhancedDoubleHasher(Random.nextLong(), Random.nextLong())
    }
}
